﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Reflection;
using System.Diagnostics;
using System.Web;
using System.Web.Hosting;
using System.Xml.Linq;
using PaymentHelper.Core.RelatedObject;
using PaymentHelper.Core.ExceptionHandler;
using PaymentHelper.Core.Extension;
using System.Security.Cryptography;
using System.Web.Compilation;
using PaymentHelper.Core.CommonUtils;
using System.Security;

[assembly: PreApplicationStartMethod(typeof(ExtensionManager), "Initialize")]
namespace PaymentHelper.Core.Extension
{
    public class ExtensionManager
    {
        private const string pathConfigFile = @"~/App_Data/PaymentHelper.Config.xml";

        private static string pathExtensions = @"~/Extensions/";
        private static Dictionary<string, bool> _installedSystemNameExtensions;
        private static IList<ExtensionDescriptor> _extensionDescriptors;
        private static bool _readyLoaded = false;
        private static CacheStorage _cacheData;

        static ExtensionManager()
        {
            _cacheData = new CacheStorage();
        }

        /// <summary>
        /// Get list of extension installed
        /// </summary>
        public static IList<ExtensionDescriptor> ExtensionDescriptors
        {
            get { return _extensionDescriptors; }
        }

        /// <summary>
        /// First initialization on start-up
        /// </summary>
        public static void Initialize()
        {
            //Load the path of extenion directory
            LoadExtensionDirectory();

            //Loadd all SystemNames of methods installed 
            LoadAllInstalledExtensions();

            //Load all Support Languages
            LoadAllSupportedLanguages();

            //Load all extension descriptors
            LoadAllDescriptors();
        }

        /// <summary>
        /// Load all descriptors from Methods folder
        /// </summary>
        /// <returns></returns>
        protected static IList<ExtensionDescriptor> LoadAllDescriptors()
        {           
            IList<ExtensionDescriptor> listDescriptors = new List<ExtensionDescriptor>();
            DirectoryInfo extensionsDiretory = null;
            try
            {
                extensionsDiretory = new DirectoryInfo(GetPath(pathExtensions));
            }
            catch (Exception e)
            {
                string message = string.Format("Can't initialize DirectoryInfo on path: {0} ({1})", GetPath(pathExtensions), e.Message);
                throw new PaymentHelperException(ExceptionLevel.Error, "ExtensionManager.LoadAllDescriptors", message);
            }

            //Ensure directories exist
            Directory.CreateDirectory(extensionsDiretory.FullName);

            //Load all descriptors
            if (extensionsDiretory.Exists)
            {
                var extensionFiles = extensionsDiretory.GetFiles("*.dll", SearchOption.AllDirectories);

                foreach (FileInfo file in extensionFiles)
                {
                    //Check rule of naming extension
                    if (!file.Directory.Name.StartsWith("PaymentHelper."))
                    {
                        continue;
                    }

                    try
                    {
                        //File dll of method
                        if (file.Name.StartsWith("PaymentHelper.", StringComparison.InvariantCultureIgnoreCase))
                        {
                            ExtensionDescriptor descriptor = LoadExtensionInfo(file.Directory);
                            if (descriptor != null)
                            {
                                //Load referenceAssembly
                                descriptor.ReferencedAssembly = LoadReferencedAssembly(file);
                                
                                //Load type of extension
                                descriptor.LoadTypeExtension();

                                //Add descriptor to result list
                                listDescriptors.Add(descriptor);
                            }
                        }
                        else //referenced dll
                        {
                            if (IsReadyAssemblyLoaded(file) == false)
                                LoadReferencedAssembly(file);
                        }
                    }
                    catch (Exception e)
                    {
                        _readyLoaded = false;
                        throw new PaymentHelperException(ExceptionLevel.Error, "ExtensionManager.LoadAllDescriptors", "Error in loading all descriptor", e);
                    }
                }
                _readyLoaded = true;
            }

            _extensionDescriptors = listDescriptors;

            return listDescriptors;
        }

        /// <summary>
        /// Load ReferencedAssembly
        /// </summary>
        /// <param name="extFile"></param>
        private static Assembly LoadReferencedAssembly(FileInfo extFile)
        {
            FileInfo copiedExtensionFile = null;

            /* MediumTrust
            AspNetHostingPermissionLevel level = GetCurrentHostingPermissionLevel();
            if (level != AspNetHostingPermissionLevel.Unrestricted)
            {
                //Ensure CopiedMethod Folder exist
                var folder = Directory.CreateDirectory(_copyFolderExtensions.FullName);
                copiedExtensionFile = InitMediumTrust(extFile, folder);
            }
            */            
            var folderCopy = new DirectoryInfo(AppDomain.CurrentDomain.DynamicDirectory);
            copiedExtensionFile = CopyToCurrentDomain(extFile, folderCopy);

            var assemblyLoaded = Assembly.Load(AssemblyName.GetAssemblyName(copiedExtensionFile.FullName));
            BuildManager.AddReferencedAssembly(assemblyLoaded);

            return assemblyLoaded;
        }

        /// <summary>
        /// Check whether an assembly is loaded
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        private static bool IsReadyAssemblyLoaded(FileInfo file)
        {
            try
            {
                string fileNameWithoutExt = Path.GetFileNameWithoutExtension(file.FullName);
                foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
                {
                    string assemblyName = assembly.FullName.Split(new[] { ',' }).FirstOrDefault();
                    if (fileNameWithoutExt.Equals(assemblyName, StringComparison.InvariantCultureIgnoreCase))
                        return true;
                }
            }
            catch (Exception exc)
            {
                Debug.WriteLine("Cannot validate whether an assembly is already loaded. " + exc);
            }
            return false;
        }

        private static FileInfo CopyToCurrentDomain(FileInfo extFile, DirectoryInfo foler)
        {
            var copiedFile = new FileInfo(Path.Combine(foler.FullName, extFile.Name));

            try
            {
                File.Copy(extFile.FullName, copiedFile.FullName, true);
            }
            catch (Exception ex)
            {
                throw new PaymentHelperException(ExceptionLevel.Error, "ExtensionManager-InitFullTrust", "Failded init Full Trust. Error: " + ex.ToString());
            }

            return copiedFile;
        }

        private static FileInfo InitMediumTrust(FileInfo extFile, DirectoryInfo folder)
        {
            bool shouldCopy = true;
            var copiedFile = new FileInfo(Path.Combine(folder.FullName, extFile.Name));

            try
            {
                //Check exist
                if (copiedFile.Exists)
                {
                    if (!IsChangedFiles(copiedFile, extFile))
                    {
                        Debug.WriteLine("No changes ... Shouldn't copy ! File: " + copiedFile.Name);
                        shouldCopy = false;
                    }
                    else //File changed --> delete old file
                    {
                        Debug.WriteLine("New plugin found, deleting the old file: '{0}'", copiedFile.Name);
                        File.Delete(copiedFile.FullName);
                    }
                }

                if (shouldCopy)
                {
                    File.Copy(extFile.FullName, copiedFile.FullName, true);
                }
            }
            catch (Exception ex)
            {
                throw new PaymentHelperException(ExceptionLevel.Error, "ExtensionManager-InitMediumTrust", "Failded init Medium Trust. Error: " + ex.ToString());
            }

            return copiedFile;
        }

        /// <summary>
        /// Load all systemName of extensions installed from XML config files
        /// </summary>
        /// <returns>List of BaseExtension</returns>
        private static void LoadAllInstalledExtensions()
        {
            Dictionary<string, bool> extensions = new Dictionary<string, bool>();

            try
            {
                XDocument config = XDocument.Load(GetPath(pathConfigFile));

                var methods = config.Document.Descendants("Extension");
                foreach (XElement xelementMethod in methods)
                {
                    string systemName = xelementMethod.Attribute("SystemName").Value;
                    bool enable = false;
                    bool.TryParse(xelementMethod.Attribute("Enable").Value, out enable);
                    extensions.Add(systemName, enable);
                }

                _installedSystemNameExtensions = extensions;
            }
            catch (FileNotFoundException fnfe)
            {
                throw new PaymentHelperException(ExceptionLevel.Error, "ExtensionManager.LoadAllInstalledSystemNameExtensions", fnfe.Message);
            }
            catch (NullReferenceException nre)
            {
                string message = string.Format("Null value from a element when load description. Detail: {0}", nre.Message);
                throw new PaymentHelperException(ExceptionLevel.Error, "ExtensionManager.LoadAllInstalledSystemNameExtensions", message);
            }
            catch (Exception e)
            {
                throw new PaymentHelperException(ExceptionLevel.Error, "ExtensionManager.LoadAllInstalledSystemNameExtensions",
                    "Error in loading descriptor", e);
            }
        }

        /// <summary>
        /// Load descriptor in folder specified
        /// </summary>
        /// <param name="infoFolder"></param>
        /// <returns></returns>
        private static ExtensionDescriptor LoadExtensionInfo(DirectoryInfo infoFolder)
        {
            if (!infoFolder.Exists)
            {
                string message = "Directory of the descriptor doesn't exist";
                throw new PaymentHelperException(ExceptionLevel.Warning, "ExtensionManager.LoadDescriptor", message);
            }

            string infoFile = infoFolder.FullName + "\\Info.xml";

            try
            {
                XDocument docInfo = XDocument.Load(infoFile);
                var xelementMethod = docInfo.Root;
                var xelementLanguage = xelementMethod.Descendants("Multilanguage").FirstOrDefault();

                ExtensionDescriptor descriptor = new ExtensionDescriptor();
                descriptor.SystemName = xelementMethod.Attribute("SystemName").Value;
                descriptor.FriendlyName = xelementMethod.Attribute("FriendlyName").Value;
                descriptor.Author = xelementMethod.Attribute("Author").Value;
                descriptor.Version = xelementMethod.Attribute("Version").Value;
                descriptor.Installed = _installedSystemNameExtensions.Keys.Contains(descriptor.SystemName);
                descriptor.Description = xelementMethod.Descendants("Description").FirstOrDefault().Value;
                descriptor.LanguageContainer = MultiLanguage.LanguageManager.GetLanguageContainer(xelementLanguage);

                if (descriptor.Installed)
                    descriptor.Enable = _installedSystemNameExtensions[descriptor.SystemName];
                else
                    descriptor.Enable = false;

                return descriptor;
            }
            catch (FileNotFoundException fnfe)
            {
                throw new PaymentHelperException(ExceptionLevel.Error, "ExtensionManager.LoadDescriptor", fnfe.Message);
            }
            catch (NullReferenceException nre)
            {
                string message = string.Format("Null value from a element when load description. Detail: {0}", nre.Message);
                throw new PaymentHelperException(ExceptionLevel.Error, "ExtensionManager.LoadDescriptor", message);
            }
            catch (Exception e)
            {
                throw new PaymentHelperException(ExceptionLevel.Error, "ExtensionManager.LoadDescriptor", "Error in loading descriptor", e);
            }
        }

        /// <summary>
        /// Load extension from the list of descriptors
        /// </summary>
        /// <typeparam name="T">T : dynamic type</typeparam>
        /// <param name="installedOnly">load only extension installed</param>
        /// <returns></returns>
        public static IList<T> LoadAllExtensions<T>(bool installedOnly, bool enableOnly) where T : class,IExtension
        {
            EnsureMethodsAreLoaded();
            if (ExtensionDescriptors == null || ExtensionDescriptors.Count == 0)
                return null;

            IList<T> listExtensions = new List<T>();
            foreach (var ext in ExtensionDescriptors)
            {
                if (typeof(T).IsAssignableFrom(ext.TypeExtension) &&
                    (!installedOnly || ext.Installed) && (!enableOnly || ext.Enable))
                {
                    listExtensions.Add(ext.GetInstance<T>());
                }
            }

            return listExtensions;
        }

        public static T LoadExtensionBySystemName<T>(string systemName, bool installedOnly, bool enableOnly) where T : class,IExtension
        {
            string keyCache = "Instance." + systemName;
            if (_cacheData.CheckExist(keyCache))
            {
                return (T)_cacheData.GetObject(keyCache);
            }

            EnsureMethodsAreLoaded();
            if (ExtensionDescriptors == null || ExtensionDescriptors.Count == 0)
                return null;

            foreach (var ext in ExtensionDescriptors)
            {
                if (typeof(T).IsAssignableFrom(ext.TypeExtension) && ext.SystemName.CompareTo(systemName) == 0
                    && (!installedOnly || ext.Installed) && (!enableOnly || ext.Enable))
                {
                    var extension = ext.GetInstance<T>();

                    //Cache:
                    _cacheData.setFileDependency(pathExtensions);
                    _cacheData.SaveObject(keyCache, extension);

                    return extension;
                }
            }

            return null;
        }

        /// <summary>
        /// Install a payment method which has systemName specified
        /// </summary>
        /// <param name="systemName"></param>
        /// <returns></returns>
        internal static bool Install(string systemName)
        {
            bool isDone = false;
            string filePath = GetPath(pathConfigFile);

            if (!File.Exists(filePath))
            {
                string message = string.Format("Can't load file {0}, installing extension failded ", filePath);
                throw new PaymentHelperException(ExceptionLevel.Warning, "ExtensionManager.Install", message);
            }

            try
            {
                XDocument docConfig = XDocument.Load(filePath);

                XElement newMethod = new XElement("Extension");
                newMethod.SetAttributeValue("SystemName", systemName);
                newMethod.SetAttributeValue("Enable", false);

                docConfig.Descendants("InstalledExtensions").SingleOrDefault().Add(newMethod);
                docConfig.Save(filePath);

                isDone = true;
                _installedSystemNameExtensions.Add(systemName, false);
            }
            catch (NullReferenceException nre)
            {
                string message = string.Format("Not found InstalledExtensions element ! {0}", nre.Message);
                throw new PaymentHelperException(ExceptionLevel.Error, "ExtensionManager.Install", message);
            }
            catch (Exception e)
            {
                throw new PaymentHelperException(ExceptionLevel.Error, "ExtensionManager.Install", "Error in installing extension", e);
            }

            return isDone;
        }

        /// <summary>
        /// UnInstall a payment method which has systemName specified
        /// </summary>
        /// <param name="systemName"></param>
        /// <returns></returns>
        internal static bool UnInstall(string systemName)
        {
            bool isDone = false;
            string filePath = GetPath(pathConfigFile);

            if (!File.Exists(filePath))
            {
                string message = string.Format("Can't load file {0}, UnInstalling extension failded !", filePath);
                throw new PaymentHelperException(ExceptionLevel.Warning, "ExtensionManager.UnInstall", message);
            }

            try
            {
                XDocument docConfig = XDocument.Load(filePath);

                var method = docConfig.Descendants("Extension").Where(m => m.Attribute("SystemName").Value.CompareTo(systemName) == 0).SingleOrDefault();
                if (method == null)
                    isDone = false;
                else
                {
                    method.Remove();
                    docConfig.Save(filePath);
                    isDone = true;
                    _installedSystemNameExtensions.Remove(systemName);
                }
            }
            catch (NullReferenceException nre)
            {
                string message = string.Format("Not found the element is {0} ! {1}", systemName, nre.Message);
                throw new PaymentHelperException(ExceptionLevel.Error, "ExtensionManager.UnInstall", message);
            }
            catch (Exception e)
            {
                throw new PaymentHelperException(ExceptionLevel.Error, "ExtensionManager.UnInstall", "Error in UnInstalling extension", e);
            }

            return isDone;
        }

        /// <summary>
        /// Load all settings of method specified
        /// </summary>
        /// <param name="paymentSystemName"></param>
        /// <returns></returns>
        internal static IList<PHSettingItem> LoadSettings(string paymentSystemName)
        {
            string keyCache = paymentSystemName + ".Settings";
            if (_cacheData.CheckExist(keyCache))
            {
                return (IList<PHSettingItem>)_cacheData.GetObject(keyCache);
            }

            string fileInfoPath = string.Format(@"{0}\{1}\Info.xml", GetPath(pathExtensions), paymentSystemName);
            if (!File.Exists(fileInfoPath))
            {
                string message = string.Format("File {0} doesn't exist! Can't load settings of {1}", fileInfoPath, paymentSystemName);
                throw new PaymentHelperException(ExceptionLevel.Warning, "ExtensionManager.LoadSettings", message);
            }

            try
            {
                XDocument docInfo = XDocument.Load(fileInfoPath);

                var settings = docInfo.Root.Descendants("Setting");

                IList<PHSettingItem> listAttribute = new List<PHSettingItem>();
                foreach (var setting in settings)
                {
                    PHSettingItem attr = new PHSettingItem();
                    attr.Name = setting.Attribute("Name").Value;
                    attr.DisplayName = setting.Attribute("DisplayName").Value;
                    attr.ValueInStr = setting.Attribute("Value").Value;
                    attr.TypeSetting = Type.GetType(setting.Attribute("Type").Value);
                    listAttribute.Add(attr);
                }

                //Cache data
                _cacheData.setFileDependency(pathExtensions + paymentSystemName + "/Info.xml");
                _cacheData.SaveObject(keyCache, listAttribute);

                return listAttribute;
            }
            catch (NullReferenceException nre)
            {
                string message = string.Format("Not found settings of the specified extension or any attributes has null value ! {0}", nre.Message);
                throw new PaymentHelperException(ExceptionLevel.Error, "ExtensionManager.LoadSettings", message);
            }
            catch (Exception e)
            {
                throw new PaymentHelperException(ExceptionLevel.Error, "ExtensionManager.LoadSettings", "Error in loading settings of extension", e);
            }
        }

        /// <summary>
        /// Save settings of payment method
        /// </summary>
        /// <param name="paymentSystemName"></param>
        /// <param name="listAttr"></param>
        /// <returns></returns>
        internal static bool SaveSettings(string paymentSystemName, IList<PHSettingItem> listAttr)
        {
            string fileInfoPath = string.Format(@"{0}\{1}\Info.xml", GetPath(pathExtensions), paymentSystemName);
            if (!File.Exists(fileInfoPath) || listAttr == null || listAttr.Count == 0)
            {
                string message = string.Format("File {0} doesn't exist! Can't load settings of {1}", fileInfoPath, paymentSystemName);
                throw new PaymentHelperException(ExceptionLevel.Warning, "ExtensionManager.SaveSettings", message); ;
            }

            try
            {
                XDocument docInfo = XDocument.Load(fileInfoPath);

                var settings = docInfo.Root.Descendants("Setting");
                if (settings != null)
                    settings.Remove();

                var settingNode = docInfo.Descendants("Settings").SingleOrDefault();

                foreach (var attr in listAttr)
                {
                    XElement newSettingNode = new XElement("Setting");
                    newSettingNode.SetAttributeValue("DisplayName", attr.DisplayName);
                    newSettingNode.SetAttributeValue("Name", attr.Name);
                    newSettingNode.SetAttributeValue("Value", attr.ValueInStr);
                    newSettingNode.SetAttributeValue("Type", attr.TypeSetting.ToString());

                    settingNode.Add(newSettingNode);
                }

                docInfo.Save(fileInfoPath);

                return true;
            }
            catch (NullReferenceException nre)
            {
                string message = string.Format("Not found Settings element ! {0}", nre.Message);
                throw new PaymentHelperException(ExceptionLevel.Error, "ExtensionManager.SaveSettings", message);
            }
            catch (Exception e)
            {
                throw new PaymentHelperException(ExceptionLevel.Error, "ExtensionManager.SaveSettings", "Error in saving settings of extension", e);
            }
        }

        private static void LoadExtensionDirectory()
        {
            try
            {
                XDocument config = XDocument.Load(GetPath(pathConfigFile));
                var extensionDirectory = config.Descendants("ExtensionDirectory");

                if (extensionDirectory == null)
                {
                    string message = "Not exist node 'ExtensionDirectory' in config file. Can't load the path of extension directory !";
                    throw new PaymentHelperException(ExceptionLevel.Warning, "ExtensionManager.LoadExtensionDirectory", message);
                }

                pathExtensions = extensionDirectory.First().Value;
            }
            catch (Exception ex)
            {
                string message = string.Format("Error when load extension directory. Detail: {0}", ex.Message);
                throw new PaymentHelperException(ExceptionLevel.Error, "ExtensionManager.LoadExtensionDirectory", message);
            }
        }

        /// <summary>
        /// Load all supported languages
        /// </summary>
        private static void LoadAllSupportedLanguages()
        {
            Dictionary<string, bool> listLanguages = new Dictionary<string, bool>();
            try
            {
                XDocument languageConfig = XDocument.Load(GetPath(pathConfigFile));
                var multiLanguage = languageConfig.Document.Descendants("Multilanguage").FirstOrDefault();
                var langs = multiLanguage.Descendants("Language");

                if (multiLanguage == null || langs.Count() == 0)
                    throw new Exception("Not exist supported language configured !");

                foreach (XElement xELanguage in langs)
                {
                    string code = xELanguage.Attribute("Code").Value;
                    bool isDefault = false;
                    bool.TryParse(xELanguage.Attribute("Default").Value, out isDefault);

                    listLanguages.Add(code, isDefault);
                }

                MultiLanguage.LanguageManager.InitMultiLanguage(listLanguages);
            }
            catch (Exception ex)
            {
                string message = string.Format("Error when load multilanguage settings. Detail: {0}", ex.Message);
                throw new PaymentHelperException(ExceptionLevel.Error, "ExtensionManager.LoadAllSupportedLanguages", message);
            }
        }

        /// <summary>
        /// Load systemname default payment flow
        /// </summary>
        /// <returns></returns>
        public static string LoadSystemNameDefaultPaymentFlow()
        {
            string keyCache = "DefaultPaymentFlow";
            if (_cacheData.CheckExist(keyCache))
            {
                return (string)_cacheData.GetObject(keyCache);
            }

            try
            {
                XDocument pfDoc = XDocument.Load(GetPath(pathConfigFile));
                var paymentFlowElement = pfDoc.Descendants("PaymentFlow").First();

                if (paymentFlowElement == null)
                    throw new Exception("Not exist PaymentFlow config !");

                var defaultPF = paymentFlowElement.Descendants("Default").First();
                if (defaultPF == null)
                    throw new Exception("Not exist default payment flow !");

                string systemNameDefaltPaymentFlow = defaultPF.Attribute("SystemName").Value;

                //Cache data
                _cacheData.setFileDependency(pathConfigFile);
                _cacheData.SaveObject(keyCache, systemNameDefaltPaymentFlow);

                return systemNameDefaltPaymentFlow;
            }
            catch (Exception ex)
            {
                string message = string.Format("Error when load default payment flow. Detail: {0}", ex.Message);
                throw new PaymentHelperException(ExceptionLevel.Error, "ExtensionManager-LoadDefaultPaymentFlow", message);
            }
        }

        /// <summary>
        /// Load system name of default payment method
        /// </summary>
        /// <returns></returns>
        public static string LoadSystemNameDefaultPaymentMethod()
        {
            string keyCache = "DefaultPaymentMethod";
            if (_cacheData.CheckExist(keyCache))
            {
                return (string)_cacheData.GetObject(keyCache);
            }

            try
            {
                XDocument pfDoc = XDocument.Load(GetPath(pathConfigFile));
                var paymentMethodConfig = pfDoc.Descendants("PaymentMethod").First();

                if (paymentMethodConfig == null)
                    throw new Exception("Not exist PaymentMethod config !");

                //Check DefaultFollow
                bool defaultFollow = bool.Parse(paymentMethodConfig.Attribute("DefaultFollow").Value);
                if (defaultFollow == false)
                    return string.Empty;

                var defaultpm = paymentMethodConfig.Descendants("Default").First();
                if (defaultpm == null)
                    throw new Exception("Not exist default payment method !");

                string defaultSystemName = defaultpm.Attribute("SystemName").Value;

                //Cache data
                _cacheData.setFileDependency(pathConfigFile);
                _cacheData.SaveObject(keyCache, defaultSystemName);

                return defaultSystemName;

            }
            catch (Exception ex)
            {
                string message = string.Format("Error when load default payment method. Detail: {0}", ex.Message);
                throw new PaymentHelperException(ExceptionLevel.Error, "ExtensionManager-LoadDefaultPaymentMethod", message);
            }
        }

        /// <summary>
        /// Get current ASPNETHostingPermissionLevel
        /// </summary>
        /// <returns></returns>
        private static AspNetHostingPermissionLevel GetCurrentHostingPermissionLevel()
        {
            var listLevel = new AspNetHostingPermissionLevel[] {
                    AspNetHostingPermissionLevel.Unrestricted,
                    AspNetHostingPermissionLevel.High,
                    AspNetHostingPermissionLevel.Medium,
                    AspNetHostingPermissionLevel.Low,
                    AspNetHostingPermissionLevel.Minimal 
                };

            foreach (AspNetHostingPermissionLevel trustLevel in listLevel)
            {
                try
                {
                    new AspNetHostingPermission(trustLevel).Demand();
                }
                catch (SecurityException)
                {
                    continue;
                }

                return trustLevel;
            }

            return AspNetHostingPermissionLevel.None;
        }
        
        /// <summary>
        /// Get physical path 
        /// </summary>
        /// <param name="sPath"></param>
        /// <returns></returns>
        private static string GetPath(string sPath)
        {
            return HostingEnvironment.MapPath(sPath);
        }

        /// <summary>
        /// Ensure all description of extensions are loadded
        /// </summary>
        private static void EnsureMethodsAreLoaded()
        {
            if (!_readyLoaded)
            {
                LoadAllDescriptors();
                _readyLoaded = true;
            }
        }

        /// <summary>
        /// Check if there're changes of the file
        /// </summary>
        /// <param name="oldFile"></param>
        /// <param name="newFile"></param>
        /// <returns></returns>
        private static bool IsChangedFiles(FileInfo oldFile, FileInfo newFile)
        {
            bool isChanged = false;

            using (HashAlgorithm hashAlg = HashAlgorithm.Create())
            {
                using (FileStream fo = new FileStream(oldFile.FullName, FileMode.Open), fn = new FileStream(newFile.FullName, FileMode.Open))
                {
                    // Calculate the hash for the files
                    byte[] hashBytesA = hashAlg.ComputeHash(fo);
                    byte[] hashBytesB = hashAlg.ComputeHash(fn);

                    isChanged = BitConverter.ToString(hashBytesA) != BitConverter.ToString(hashBytesB);
                }
            }

            return isChanged;
        }
    }
}
